﻿namespace FormStateKeeper
{
    using System.Collections.Specialized;
    using System.IO;
    using System.Web;
    using System.Web.UI;

    /// <summary>
    /// This http handler will render a small page that will reconstruct the form as it was before session timeout, using hidden fields.
    /// The page will submit itself upon loading, meaning that the user will barely see it.
    /// </summary>
    public class FormStateRestoreHandler : BaseHttpHandler
    {
        private readonly FormState _state;

        /// <summary>
        /// Initializes a new instance of the <see cref="FormStateRestoreHandler"/> class.
        /// </summary>
        /// <param name="state">The form state to restore.</param>
        public FormStateRestoreHandler(FormState state)
        {
            _state = state;
        }

        /// <summary>
        /// Gets a value indicating whether another request can use this IHttpHandler instance.
        /// </summary>
        public override bool IsReusable
        {
            get { return false; }
        }

        /// <summary>
        /// Gets the form state.
        /// </summary>
        /// <value>The form state.</value>
        protected internal FormState FormState
        {
            get { return _state; }
        }

        /// <summary>
        /// Processes the web request - this is where the page is rendered.
        /// </summary>
        /// <param name="context">The context.</param>
        public override void ProcessRequest(HttpContextBase context)
        {
            try
            {
                using (HtmlTextWriter writer = CreateHtmlTextWriter(context.Response.Output, context.Request.Browser))
                {
                    this.Render(writer);
                }
            }
            finally
            {
                _state.Delete();
            }
        }

        /// <summary>
        /// Renders a hidden field.
        /// </summary>
        /// <param name="writer">The writer to use.</param>
        /// <param name="name">The name of the hidden field.</param>
        /// <param name="value">The value of the hidden field.</param>
        protected static void RenderHiddenField(HtmlTextWriter writer, string name, string value)
        {
            writer.AddAttribute(HtmlTextWriterAttribute.Type, "hidden");
            writer.AddAttribute(HtmlTextWriterAttribute.Name, name);
            writer.AddAttribute(HtmlTextWriterAttribute.Value, value);
            writer.RenderBeginTag(HtmlTextWriterTag.Input);
            writer.RenderEndTag(); // INPUT
        }

        /// <summary>
        /// Renders a submit button.
        /// </summary>
        /// <param name="writer">The writer to use.</param>
        /// <param name="text">The text of the button.</param>
        protected static void RenderSubmitButton(HtmlTextWriter writer, string text)
        {
            writer.AddAttribute(HtmlTextWriterAttribute.Type, "submit");
            writer.AddAttribute(HtmlTextWriterAttribute.Value, text);
            writer.RenderBeginTag(HtmlTextWriterTag.Input);
            writer.RenderEndTag(); // INPUT
        }

        /// <summary>
        /// Gets a HtmlTextWriter to write output to, based on a TextWriter.
        /// </summary>
        /// <param name="writer">The Text writer holding the output stream.</param>
        /// <param name="browser">The browser capabilities of the client browser.</param>
        /// <returns>Writer to writer output to.</returns>
        protected static HtmlTextWriter CreateHtmlTextWriter(TextWriter writer, HttpBrowserCapabilitiesBase browser)
        {
            if (browser == null)
                return new HtmlTextWriter(writer);
            return browser.CreateHtmlTextWriter(writer);
        }

        /// <summary>
        /// Renders a small page that will resubmit the saved form
        /// </summary>
        /// <param name="writer">The writer to use for rendering.</param>
        protected virtual void Render(HtmlTextWriter writer)
        {
            writer.RenderBeginTag(HtmlTextWriterTag.Html);
            writer.RenderBeginTag(HtmlTextWriterTag.Head);
            writer.RenderBeginTag(HtmlTextWriterTag.Title);
            writer.Write("Restoring form");
            writer.RenderEndTag(); // TITLE
            writer.RenderEndTag(); // HEAD

            writer.AddAttribute("onload", "document.forms[0].submit();");
            writer.RenderBeginTag(HtmlTextWriterTag.Body);

            writer.AddAttribute("method", "post");
            writer.RenderBeginTag(HtmlTextWriterTag.Form);

            NameValueCollection form = this.FormState.Form;
            foreach (string name in form.Keys)
            {
                if (name == "__ASYNCPOST")
                {
                    RenderHiddenField(writer, name, "false"); // Handle update panel issue
                }
                else
                {
                    string[] values = form[name].Split(',');
                    foreach (string value in values)
                    {
                        RenderHiddenField(writer, name, value);
                    }
                }
            }

            writer.AddAttribute(HtmlTextWriterAttribute.Align, "center");
            writer.RenderBeginTag(HtmlTextWriterTag.P);
            writer.Write("You should be redirected in a moment.");
            writer.WriteBreak();
            writer.Write("If nothing happens, please click ");
            RenderSubmitButton(writer, "Submit");
            writer.RenderEndTag(); // P

            writer.RenderEndTag(); // FORM
            writer.RenderEndTag(); // BODY
            writer.RenderEndTag(); // HTML
        }
    }
}
